/********************************************************************************
 * Copyright (c) 2011-2017 Red Hat Inc. and/or its affiliates and others
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License 1.0 which is available at
 * http://www.eclipse.org/legal/epl-v10.html.
 *
 * SPDX-License-Identifier: EPL-1.0
 ********************************************************************************/
package org.eclipse.ceylon.ide.eclipse.code.preferences;

import java.lang.reflect.InvocationTargetException;

import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IWorkspaceRunnable;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.internal.ui.IJavaHelpContextIds;
import org.eclipse.jdt.internal.ui.JavaPlugin;
import org.eclipse.jdt.internal.ui.actions.WorkbenchRunnableAdapter;
import org.eclipse.jdt.internal.ui.dialogs.StatusUtil;
import org.eclipse.jdt.internal.ui.preferences.PreferencesMessages;
import org.eclipse.jdt.internal.ui.util.ExceptionHandler;
import org.eclipse.jdt.internal.ui.wizards.IStatusChangeListener;
import org.eclipse.jface.dialogs.Dialog;
import org.eclipse.jface.dialogs.IDialogSettings;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.dialogs.ProgressMonitorDialog;
import org.eclipse.jface.preference.IPreferencePageContainer;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Label;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.dialogs.PropertyPage;
import org.eclipse.ui.preferences.IWorkbenchPreferenceContainer;

import org.eclipse.ceylon.ide.eclipse.core.builder.CeylonBuilder;
import org.eclipse.ceylon.ide.eclipse.core.builder.CeylonNature;
import org.eclipse.ceylon.ide.eclipse.ui.CeylonPlugin;

public class CeylonBuildPathsPropertiesPage extends PropertyPage implements IStatusChangeListener {
    
    public static final String ID = CeylonPlugin.PLUGIN_ID + ".preferences.paths";

    private static final String PAGE_SETTINGS = "BuildPathsPropertyPage";
    private static final String INDEX = "pageIndex";
    
    private boolean fBlockOnApply = false;
    
    private CeylonBuildPathsBlock fBuildPathsBlock;

    public void statusChanged(IStatus status) {
        setValid(!status.matches(IStatus.ERROR));
        StatusUtil.applyToStatusLine(this, status);
    }
    
    @Override
    public boolean performOk() {
        if (fBuildPathsBlock != null) {
            getSettings().put(INDEX, fBuildPathsBlock.getPageIndex());
            boolean overwriteCeylonConfig = false;
            if (!fBuildPathsBlock.wasInSyncWithCeylonConfigWhenOpening()
                    && !fBuildPathsBlock.isInSyncWithCeylonConfig()) {
                if (!MessageDialog.openQuestion(getShell(),
                        "Setting Ceylon Build Path", 
                        "The Ceylon configuration file (.ceylon/config) is not synchronized with the current build path settings.\n" +
                        "Do you want to overwrite the configuration file with the settings defined here ?")) {
                    return false;
                }
                overwriteCeylonConfig = true;
            }
            if (fBuildPathsBlock.hasChangesInDialog() || 
                    fBuildPathsBlock.isClassfileMissing() ||
                    overwriteCeylonConfig) {
                IWorkspaceRunnable runnable= new IWorkspaceRunnable() {
                    public void run(IProgressMonitor monitor)
                            throws CoreException, OperationCanceledException {
                        fBuildPathsBlock.configureJavaProject(monitor);
                    }
                };
                WorkbenchRunnableAdapter op= new WorkbenchRunnableAdapter(runnable);
                if (fBlockOnApply) {
                    try {
                        new ProgressMonitorDialog(getShell()).run(true, true, op);
                    }
                    catch (InvocationTargetException e) {
                        ExceptionHandler.handle(e, getShell(), 
                                PreferencesMessages.BuildPathsPropertyPage_error_title, 
                                PreferencesMessages.BuildPathsPropertyPage_error_message);
                        return false;
                    }
                    catch (InterruptedException e) {
                        return false;
                    }
                }
                else {
                    op.runAsUserJob(PreferencesMessages.BuildPathsPropertyPage_job_title, null);
                }
            }
        }
        return true;

    }

    @Override
    protected Control createContents(Composite parent) {
        // ensure the page has no special buttons
        noDefaultAndApplyButton();

        IProject project = getProject();
        Control result;
        if (project == null) {
            result = createWithoutCeylon(parent);
        }
        else if (!project.isOpen()) {
            result = createForClosedProject(parent);
        } 
        else if (!isCeylonProject(project)) {
            result = createWithoutCeylon(parent);
        } 
        else  {
            result = createWithCeylon(parent, project);
        }
        Dialog.applyDialogFont(result);
        return result;
    }

    /*
     * @see org.eclipse.jface.dialogs.IDialogPage#createControl(org.eclipse.swt.widgets.Composite)
     */
    @Override
    public void createControl(Composite parent) {
        super.createControl(parent);
        PlatformUI.getWorkbench().getHelpSystem().setHelp(getControl(), 
                IJavaHelpContextIds.BUILD_PATH_PROPERTY_PAGE);
    }

    private IDialogSettings getSettings() {
        IDialogSettings javaSettings = 
                CeylonPlugin.getInstance().getDialogSettings();
        IDialogSettings pageSettings = 
                javaSettings.getSection(PAGE_SETTINGS);
        if (pageSettings == null) {
            pageSettings = 
                    javaSettings.addNewSection(PAGE_SETTINGS);
            pageSettings.put(INDEX, 1);
        }
        return pageSettings;
    }

    @Override
    public boolean okToLeave() {
        if (fBuildPathsBlock != null && 
                fBuildPathsBlock.hasChangesInDialog()) {
            String title = PreferencesMessages.BuildPathsPropertyPage_unsavedchanges_title;
            String message = PreferencesMessages.BuildPathsPropertyPage_unsavedchanges_message;
            String[] buttonLabels = new String[] {
                    PreferencesMessages.BuildPathsPropertyPage_unsavedchanges_button_save,
                    PreferencesMessages.BuildPathsPropertyPage_unsavedchanges_button_discard,
                    PreferencesMessages.BuildPathsPropertyPage_unsavedchanges_button_ignore
            };
            MessageDialog dialog= new MessageDialog(getShell(), 
                    title, null, message, 
                    MessageDialog.QUESTION, buttonLabels, 0);
            int res= dialog.open();
            if (res == 0) { //save
                fBlockOnApply= true;
                return performOk() && super.okToLeave();
            } else if (res == 1) { // discard
                fBuildPathsBlock.init(JavaCore.create(getProject()), 
                        null, null, 
                        CeylonBuilder.compileToJava(getProject()));
            } else {
                // keep unsaved
            }
        }
        return super.okToLeave();
    }
    
    @Override
    public void setVisible(boolean visible) {
        if (fBuildPathsBlock != null) {
            if (visible) {
                if (!fBuildPathsBlock.hasChangesInDialog() && 
                        fBuildPathsBlock.hasChangesInClasspathFile()) {
                    fBuildPathsBlock.init(JavaCore.create(getProject()), 
                            null, null, 
                            CeylonBuilder.compileToJava(getProject()));
                }
            }
        }
        super.setVisible(visible);
    }


    /*
     * Content for valid projects.
     */
    private Control createWithCeylon(Composite parent, IProject project) {
        IWorkbenchPreferenceContainer pageContainer= null;
        IPreferencePageContainer container= getContainer();
        if (container instanceof IWorkbenchPreferenceContainer) {
            pageContainer= (IWorkbenchPreferenceContainer) container;
        }

        fBuildPathsBlock= new CeylonBuildPathsBlock(this, 
                getSettings().getInt(INDEX), pageContainer);
        fBuildPathsBlock.init(JavaCore.create(project), 
                null, null, 
                CeylonBuilder.compileToJava(project));
        return fBuildPathsBlock.createControl(parent);
    }

    /*
     * Content for non-Java projects.
     */
    private Control createWithoutCeylon(Composite parent) {
        Label label = new Label(parent, SWT.LEFT);
        label.setText("Not a Ceylon project");
        fBuildPathsBlock= null;
        setValid(true);
        return label;
    }

    /*
     * Content for closed projects.
     */
    private Control createForClosedProject(Composite parent) {
        Label label= new Label(parent, SWT.LEFT);
        label.setText("Information not available for closed project.");
        fBuildPathsBlock= null;
        setValid(true);
        return label;
    }

    private IProject getProject() {
        IAdaptable adaptable = getElement();
        return adaptable == null ? null : 
            (IProject) adaptable.getAdapter(IProject.class);
    }

    private boolean isCeylonProject(IProject proj) {
        try {
            return proj.hasNature(JavaCore.NATURE_ID) &&
                    proj.hasNature(CeylonNature.NATURE_ID);
        } catch (CoreException e) {
            JavaPlugin.log(e);
        }
        return false;
    }


}